import os
import pathlib
import sqlite3
import json
from typing import List, Dict, Any, Optional
from dotenv import load_dotenv
import time
import uvicorn
import hashlib
import jwt
from datetime import datetime, timedelta
import threading
import concurrent.futures
from functools import wraps

from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

# 创建线程池执行器用于并行处理
thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=10)

# 加载环境变量
dotenv_path = pathlib.Path(__file__).parent / '.env'
if dotenv_path.exists():
    load_dotenv(dotenv_path)

# 创建FastAPI应用
app = FastAPI(title="计算机网络学习助手", description="一个基于FastAPI的计算机网络学习助手", version="1.0")

# 添加CORS中间件
origins = ["*"]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 初始化SQLite数据库
DB_PATH = "database.db"

# 数据库连接管理器
class DatabaseManager:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super(DatabaseManager, cls).__new__(cls)
                cls._instance.conn = None
                cls._instance.cursor = None
        return cls._instance
        
    def connect(self):
        if self.conn is None:
            self.conn = sqlite3.connect(DB_PATH, check_same_thread=False)
            self.cursor = self.conn.cursor()
        return self.conn, self.cursor
        
    def close(self):
        if self.conn is not None:
            self.conn.close()
            self.conn = None
            self.cursor = None

# 创建数据库和表
def init_database():
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    # 创建用户表
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT UNIQUE NOT NULL,
        name TEXT NOT NULL,
        password TEXT NOT NULL,
        created_at REAL NOT NULL
    )
    ''')
    
    # 创建会话表
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS conversations (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER NOT NULL,
        title TEXT NOT NULL,
        created_at REAL NOT NULL,
        updated_at REAL NOT NULL,
        FOREIGN KEY (user_id) REFERENCES users (id)
    )
    ''')

    # 创建聊天历史表
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS chat_history (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER NOT NULL,
        conversation_id INTEGER NOT NULL,
        user TEXT NOT NULL,
        message TEXT NOT NULL,
        ai TEXT NOT NULL,
        timestamp REAL NOT NULL,
        FOREIGN KEY (user_id) REFERENCES users (id),
        FOREIGN KEY (conversation_id) REFERENCES conversations (id)
    )
    ''')
    
    # 创建问题记录索引以提高查询性能
    cursor.execute('''
        CREATE INDEX IF NOT EXISTS idx_chat_history_user_id ON chat_history (user_id)
    ''')
    
    conn.commit()
    db.close()

# 初始化数据库
init_database()

# 配置JWT
SECRET_KEY = "your-secret-key"  # 在生产环境中应该使用环境变量
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# 密码上下文
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# 并行处理装饰器
import asyncio
def parallel(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        loop = asyncio.get_event_loop()
        return await loop.run_in_executor(thread_pool, lambda: func(*args, **kwargs))
    return wrapper

# 挂载public目录
public_dir = pathlib.Path(__file__).parent / "public"
# 只挂载到/public路径，支持HTML文件作为默认页面
app.mount("/public", StaticFiles(directory=str(public_dir), html=True), name="public")

# 挂载data/videos目录，让前端可以直接访问视频文件
video_dir = pathlib.Path(__file__).parent / "data" / "videos"
app.mount("/data/videos", StaticFiles(directory=str(video_dir)), name="videos")

# 添加根路径路由，将根请求重定向到首页
@app.get("/")
def redirect_to_home():
    return RedirectResponse(url="/public/index.html")

# 定义请求模型
class RegisterRequest(BaseModel):
    username: str
    password: str
    name: str

class LoginRequest(BaseModel):
    username: str
    password: str

class ChatRequest(BaseModel):
    message: str
    conversation_id: int

class CreateConversationRequest(BaseModel):
    title: str

# 创建访问令牌
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# 获取当前用户
@parallel
def get_current_user(request: Request):
    token = request.headers.get("Authorization")
    if not token:
        raise HTTPException(status_code=401, detail="未提供认证令牌")
    try:
        # 提取token（去除"Bearer "前缀）
        if token.startswith("Bearer "):
            token = token[7:]
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401, detail="无效的认证令牌")
        
        # 查找用户
        db = DatabaseManager()
        conn, cursor = db.connect()
        cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
        user_row = cursor.fetchone()
        db.close()
        
        if not user_row:
            raise HTTPException(status_code=401, detail="用户不存在")
        
        # 构建用户对象（不返回密码信息）
        user = {
            "id": user_row[0],
            "username": user_row[1],
            "name": user_row[2],
            "created_at": user_row[4]
        }
        
        return user
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="无效的认证令牌")

# 用户注册API
@app.post("/api/register")
@parallel
def register(register_data: RegisterRequest):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 检查用户是否已存在
        cursor.execute("SELECT * FROM users WHERE username = ?", (register_data.username,))
        if cursor.fetchone():
            raise HTTPException(status_code=400, detail="用户名已存在")
        
        # 使用更安全的加密方式
        hashed_password = pwd_context.hash(register_data.password)
        
        # 创建新用户
        created_at = time.time()
        cursor.execute(
            "INSERT INTO users (username, name, password, created_at) VALUES (?, ?, ?, ?)",
            (register_data.username, register_data.name, hashed_password, created_at)
        )
        
        conn.commit()
        return JSONResponse(status_code=201, content={"message": "注册成功"})
    except Exception as e:
        conn.rollback()
        raise
    finally:
        db.close()

# 用户登录API
@app.post("/api/login")
@parallel
def login(login_data: LoginRequest):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 查找用户
        cursor.execute("SELECT * FROM users WHERE username = ?", (login_data.username,))
        user_row = cursor.fetchone()
        
        if not user_row:
            raise HTTPException(status_code=400, detail="用户名或密码错误")
        
        # 验证密码（支持旧的SHA256和新的bcrypt两种方式）
        password_match = False
        if user_row[3].startswith('$2b$'):  # bcrypt哈希格式
            password_match = pwd_context.verify(login_data.password, user_row[3])
        else:  # 旧的SHA256格式
            password_match = (hashlib.sha256(login_data.password.encode()).hexdigest() == user_row[3])
        
        if not password_match:
            raise HTTPException(status_code=400, detail="用户名或密码错误")
        
        # 创建访问令牌
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": user_row[1]},
            expires_delta=access_token_expires
        )
        
        return {"access_token": access_token, "token_type": "bearer", "user_id": user_row[0], "name": user_row[2]}
    finally:
        db.close()

# LangChain问答接口
@app.post("/api/chat")
@parallel
def chat_endpoint(request_data: ChatRequest, current_user: dict = Depends(get_current_user)):
    message = request_data.message
    if not message:
        raise HTTPException(status_code=400, detail="Message is required")
    
    # 检查对话是否存在
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 检查对话是否属于当前用户
        cursor.execute("SELECT id FROM conversations WHERE id = ? AND user_id = ?", 
                      (request_data.conversation_id, current_user["id"]))
        conversation = cursor.fetchone()
        
        # 如果对话不存在，创建一个新对话
        if not conversation:
            # 创建新对话
            timestamp = time.time()
            cursor.execute(
                "INSERT INTO conversations (user_id, title, created_at, updated_at) VALUES (?, ?, ?, ?)",
                (current_user["id"], message[:20] + (message[:20] and message[20:] and '...'), timestamp, timestamp)
            )
            request_data.conversation_id = cursor.lastrowid
        else:
            # 更新对话的更新时间
            timestamp = time.time()
            cursor.execute("UPDATE conversations SET updated_at = ? WHERE id = ?", 
                          (timestamp, request_data.conversation_id))
        
        # 生成AI响应
        response = generate_ai_response(message)
        
        # 保存聊天记录到数据库
        cursor.execute(
            "INSERT INTO chat_history (user_id, conversation_id, user, message, ai, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
            (current_user["id"], request_data.conversation_id, current_user["name"], message, response, timestamp)
        )
        conn.commit()
        
        return {"response": response}
    except Exception as e:
        conn.rollback()
        print(f"Error saving chat: {str(e)}")
        # 即使保存失败，也返回AI响应，让用户体验更流畅
        response = generate_ai_response(message)
        return {"response": response}
    finally:
        db.close()

# 创建新对话
@app.post("/api/create-conversation")
@parallel
def create_conversation(request_data: CreateConversationRequest, current_user: dict = Depends(get_current_user)):
    timestamp = time.time()
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 在conversations表中创建新对话
        cursor.execute(
            "INSERT INTO conversations (user_id, title, created_at, updated_at) VALUES (?, ?, ?, ?)",
            (current_user["id"], request_data.title, timestamp, timestamp)
        )
        
        # 获取新创建的对话ID
        conversation_id = cursor.lastrowid
        conn.commit()
        
        return {"conversation_id": conversation_id, "title": request_data.title}
    except Exception as e:
        conn.rollback()
        raise HTTPException(status_code=500, detail=f"创建对话失败: {str(e)}")
    finally:
        db.close()

# 获取用户的所有对话
@app.get("/api/conversations")
@parallel
def get_conversations(current_user: dict = Depends(get_current_user)):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 查询用户的所有对话，按更新时间倒序排列
        cursor.execute(
            "SELECT id, title, created_at, updated_at FROM conversations WHERE user_id = ? ORDER BY updated_at DESC",
            (current_user["id"],)
        )
        rows = cursor.fetchall()
        
        # 构建对话列表
        conversations = [
            {
                "id": row[0],
                "title": row[1],
                "created_at": row[2],
                "updated_at": row[3]
            }
            for row in rows
        ]
        
        return {"conversations": conversations}
    finally:
        db.close()

# 删除对话
@app.delete("/api/conversation/{conversation_id}")
@parallel
def delete_conversation(conversation_id: int, current_user: dict = Depends(get_current_user)):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 检查对话是否属于当前用户
        cursor.execute("SELECT id FROM conversations WHERE id = ? AND user_id = ?", 
                      (conversation_id, current_user["id"]))
        conversation = cursor.fetchone()
        
        if not conversation:
            raise HTTPException(status_code=404, detail="对话不存在或不属于您")
        
        # 先删除相关的聊天记录
        cursor.execute("DELETE FROM chat_history WHERE conversation_id = ?", (conversation_id,))
        
        # 再删除对话
        cursor.execute("DELETE FROM conversations WHERE id = ?", (conversation_id,))
        
        conn.commit()
        return {"success": True, "message": "对话已删除"}
    except Exception as e:
        conn.rollback()
        raise HTTPException(status_code=500, detail=str(e))
    finally:
        db.close()

# 更新对话标题
@app.put("/api/conversation/{conversation_id}/title")
@parallel
def update_conversation_title(conversation_id: int, request_data: dict, current_user: dict = Depends(get_current_user)):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 检查对话是否属于当前用户
        cursor.execute("SELECT id FROM conversations WHERE id = ? AND user_id = ?", 
                      (conversation_id, current_user["id"]))
        conversation = cursor.fetchone()
        
        if not conversation:
            raise HTTPException(status_code=404, detail="对话不存在或不属于您")
        
        # 获取新标题
        new_title = request_data.get('title')
        if not new_title:
            raise HTTPException(status_code=400, detail="标题不能为空")
        
        # 更新对话标题和更新时间
        timestamp = time.time()
        cursor.execute(
            "UPDATE conversations SET title = ?, updated_at = ? WHERE id = ?", 
            (new_title, timestamp, conversation_id)
        )
        
        conn.commit()
        return {"success": True, "message": "对话标题已更新"}
    except Exception as e:
        conn.rollback()
        raise HTTPException(status_code=500, detail=str(e))
    finally:
        db.close()

# 清空历史记录
@app.post("/api/clear-history")
@parallel
def clear_history(current_user: dict = Depends(get_current_user)):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 删除用户的所有对话
        cursor.execute("DELETE FROM conversations WHERE user_id = ?", (current_user["id"],))
        
        # 删除用户的所有聊天记录
        cursor.execute("DELETE FROM chat_history WHERE user_id = ?", (current_user["id"],))
        
        conn.commit()
        return {"success": True, "message": "历史记录已清空"}
    except Exception as e:
        conn.rollback()
        raise HTTPException(status_code=500, detail=str(e))
    finally:
        db.close()

# 获取特定对话的所有消息
@app.get("/api/conversation/{conversation_id}/messages")
@parallel
def get_conversation_messages(conversation_id: int, current_user: dict = Depends(get_current_user)):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 查询特定对话的所有消息，按时间戳正序排列
        cursor.execute(
            "SELECT message, ai, timestamp FROM chat_history WHERE user_id = ? AND conversation_id = ? ORDER BY timestamp ASC",
            (current_user["id"], conversation_id)
        )
        rows = cursor.fetchall()
        
        # 构建消息列表
        messages = [
            {
                "message": row[0],
                "ai": row[1],
                "timestamp": row[2]
            }
            for row in rows
        ]
        
        return {"messages": messages}
    finally:
        db.close()

# 获取用户聊天历史（向后兼容）
@app.get("/api/chat-history")
@parallel
def get_chat_history(current_user: dict = Depends(get_current_user)):
    db = DatabaseManager()
    conn, cursor = db.connect()
    
    try:
        # 查询用户的聊天历史，按时间戳倒序排列
        cursor.execute(
            "SELECT user_id, user, message, ai, timestamp FROM chat_history WHERE user_id = ? ORDER BY timestamp ASC",
            (current_user["id"],)
        )
        rows = cursor.fetchall()
        
        # 构建历史记录列表
        user_history = [
            {
                "user_id": row[0],
                "user": row[1],
                "message": row[2],
                "ai": row[3],
                "timestamp": row[4]
            }
            for row in rows
        ]
        
        return {"history": user_history}
    finally:
        db.close()

@app.post("/api/clear-history")
@parallel
def clear_chat_history(current_user: dict = Depends(get_current_user)):
    db = DatabaseManager()
    conn, cursor = db.connect()
    try:
        cursor.execute("DELETE FROM chat_history WHERE user_id = ?", (current_user["id"],))
        conn.commit()
        return {"status": "success", "message": "聊天历史已清空"}
    except Exception as e:
        conn.rollback()
        raise HTTPException(status_code=500, detail=f"清空历史记录失败: {str(e)}")
    finally:
        db.close()

# 文件列表API - 课本
@app.get("/api/files/textbooks", response_model=List[Dict[str, Any]])
def get_textbooks():
    books_dir = pathlib.Path(__file__).parent / "data" / "books"
    files = []
    
    # 获取主要课本
    if books_dir.exists():
        book_files = [
            file for file in os.listdir(books_dir)
            if file.endswith('.pdf')
        ]
        
        files = [
            {
                "name": file,
                "path": f"/data/books/{file}",
                "size": os.path.getsize(books_dir / file),
                "type": "textbook"
            }
            for file in book_files
        ]
    
    return files

# 课本阅读页面路由
@app.get("/textbook")
def textbook_page(file: Optional[str] = None):
    print(f"接收到的file参数: {file}")
    if file:
        # 记录接收到的文件路径
        print(f"尝试打开课本: {file}")
    return FileResponse("public/textbook.html")

# 视频学习页面路由
@app.get("/video")
def video_page():
    return FileResponse("public/video.html")

# 文件列表API - 视频
@app.get("/api/files/videos", response_model=List[Dict[str, Any]])
def get_videos():
    video_dir = pathlib.Path(__file__).parent / "data" / "videos"
    if not video_dir.exists():
        return []
    
    video_extensions = ['.mp4', '.avi', '.mov', '.wmv', '.mkv']
    video_files = [
        file for file in os.listdir(video_dir)
        if any(file.lower().endswith(ext) for ext in video_extensions)
    ]
    
    files = [
        {
            "name": file,
            "path": f"/data/videos/{file}",
            "size": os.path.getsize(video_dir / file),
            "type": "video"
        }
        for file in video_files
    ]
    
    return files

# 模拟AI响应函数
def generate_ai_response(message: str) -> str:
    # 这里应该集成真实的LangChain模型
    responses = [
        "根据计算机网络课本的内容，这个问题涉及到...",
        "参考相关学习资料，这里详细讲解了...",
        "这个问题在教材中有详细说明，建议您查看相关内容。",
        "根据课程内容，这个知识点可以这样理解..."
    ]
    return responses[hash(message) % len(responses)]

# 添加PDF文件特定访问路由
@app.get("/pdf-books/{filename}")
async def get_pdf_book(filename: str):
    """提供PDF文件访问的特定路由"""
    from fastapi.responses import FileResponse
    import os
    
    pdf_path = pathlib.Path(__file__).parent / "data" / "books" / filename
    
    if not os.path.exists(pdf_path):
        return {"error": "File not found"}
    
    # 确保只有PDF文件可以被访问
    if not filename.lower().endswith('.pdf'):
        return {"error": "Only PDF files are allowed"}
    
    try:
        return FileResponse(
            pdf_path,
            media_type="application/pdf",
            filename=filename
        )
    except Exception as e:
        return {"error": f"Failed to read file: {str(e)}"}

# 已经在文件开头挂载了public目录
# 移除重复的根路径挂载以避免覆盖API路由

# 启动服务器
if __name__ == "__main__":
    port = int(os.environ.get("PORT", 3001))
    uvicorn.run("main:app", host="0.0.0.0", port=port, reload=True)